Synopsis: Polymorphic Associations
Let’s look at an antipattern that originates from multiple inheritance relationships.
We'll cover the following
Let’s allow users to make comments on bugs. A given bug may have many comments, but any given comment must pertain to a single bug. So, there’s a one-to-many relationship between Bugs
and Comments
. The Entity-Relationship Diagram for this kind of simple association is shown below.
The following SQL shows how we would create this table:
However, we may have two tables we can comment on. Bugs
and FeatureRequests
are similar entities, although we may store them as separate tables (see Concrete table inheritance).
We’d like to store Comments
in a single table regardless of whether they pertain to either type of issue (a bug or a feature) but we can’t declare a foreign key that references multiple parent tables. For example, the following declaration doesn’t make sense:
Let’s try it in the following playground.
Developers also try to write invalid SQL to query multiple tables, such as the following:
But we can’t join to a different table in every row in SQL. SQL syntax requires that we name all the tables at the exact time we submit the query. The tables cannot vary during the query. What’s wrong with this picture, and how do we solve it?
This kind of association is illustrated in the Entity-Relationship Diagram below. This is also sometimes called a “promiscuous association” because it can reference multiple tables.
Objective: Reference multiple parents#
The Scarecrow in The Wizard of Oz gives Dorothy uncertain directions when she asks which fork in the road she should take to get to the Emerald City. What should be a clear answer to Dorothy’s simple question just confuses her when the Scarecrow tries to give her two answers at once.
This kind of association is illustrated in the Entity-Relationship Diagram in the figure below. The foreign key in the child table forks, so that a row in the Comments
table matches either a row in the Bugs
table or a row in the FeatureRequests
table. The curved arc in the diagram indicates an exclusive choice: a given comment must reference either one bug or one feature request.
Legitimate uses of the antipattern#
It is normally best to avoid the Polymorphic Associations antipattern. Instead, we can use constraints like foreign keys to ensure referential integrity. Polymorphic Associations often rely too much on application code instead of metadata.
We may find that this antipattern is unavoidable if we use an object-relational programming framework such as Hibernate. Such a framework may mitigate the risks introduced by Polymorphic Associations by encapsulating application logic to maintain referential integrity.
If we choose a mature and reputable framework, we can be confident that its designers have written the code to implement the association without error. However, if we are implementing Polymorphic Associations from scratch without the aid of a framework, it is as if we’re reinventing the wheel.